/*      */ package com.zimbra.cs.mailbox;
/*      */ 
/*      */ import com.google.common.annotations.VisibleForTesting;
/*      */ import com.google.common.base.Preconditions;
/*      */ import com.zimbra.common.account.Key.AccountBy;
/*      */ import com.zimbra.common.localconfig.DebugConfig;
/*      */ import com.zimbra.common.localconfig.KnownKey;
/*      */ import com.zimbra.common.localconfig.LC;
/*      */ import com.zimbra.common.service.ServiceException;
/*      */ import com.zimbra.common.stats.Counter;
/*      */ import com.zimbra.common.stats.StopWatch;
/*      */ import com.zimbra.common.util.Log;
/*      */ import com.zimbra.common.util.ZimbraLog;
/*      */ import com.zimbra.cs.account.Account;
/*      */ import com.zimbra.cs.account.AccountServiceException;
/*      */ import com.zimbra.cs.account.NamedEntry;
/*      */ import com.zimbra.cs.account.Provisioning;
/*      */ import com.zimbra.cs.db.DbMailbox;
/*      */ import com.zimbra.cs.db.DbPool;
/*      */ import com.zimbra.cs.db.DbPool.DbConnection;
/*      */ import com.zimbra.cs.extension.ExtensionUtil;
/*      */ import com.zimbra.cs.index.IndexStore;
/*      */ import com.zimbra.cs.redolog.op.CreateMailbox;
/*      */ import com.zimbra.cs.stats.ZimbraPerf;
/*      */ import com.zimbra.cs.util.AccountUtil;
/*      */ import com.zimbra.cs.util.Zimbra;
/*      */ import java.lang.ref.SoftReference;
/*      */ import java.util.ArrayList;
/*      */ import java.util.Collection;
/*      */ import java.util.HashMap;
/*      */ import java.util.HashSet;
/*      */ import java.util.Iterator;
/*      */ import java.util.LinkedHashMap;
/*      */ import java.util.List;
/*      */ import java.util.Map;
/*      */ import java.util.Map.Entry;
/*      */ import java.util.Set;
/*      */ import java.util.concurrent.ConcurrentHashMap;
/*      */ import java.util.concurrent.CopyOnWriteArrayList;
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ public class MailboxManager
/*      */ {
/*      */   public static enum FetchMode
/*      */   {
/*   56 */     AUTOCREATE, 
/*   57 */     DO_NOT_AUTOCREATE, 
/*   58 */     ONLY_IF_CACHED;
/*      */     
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */     private FetchMode() {}
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*   80 */   private ConcurrentHashMap<String, MailboxMaintenance> maintenanceLocks = new ConcurrentHashMap();
/*      */   
/*   82 */   private CopyOnWriteArrayList<Listener> mListeners = new CopyOnWriteArrayList();
/*      */   private static MailboxManager sInstance;
/*      */   
/*   85 */   public void addListener(Listener listener) { assert (!this.mListeners.contains(listener));
/*   86 */     this.mListeners.add(listener);
/*      */   }
/*      */   
/*      */   public void removeListener(Listener listener) {
/*   90 */     assert (this.mListeners.contains(listener));
/*   91 */     this.mListeners.remove(listener);
/*      */   }
/*      */   
/*      */   private void notifyMailboxAvailable(Mailbox mbox) {
/*   95 */     if (ZimbraLog.mbxmgr.isInfoEnabled())
/*   96 */       ZimbraLog.mbxmgr.info("Mailbox " + mbox.getId() + " account " + mbox.getAccountId() + " AVAILABLE");
/*   97 */     for (Listener listener : this.mListeners)
/*   98 */       listener.mailboxAvailable(mbox);
/*      */   }
/*      */   
/*      */   private void notifyMailboxLoaded(Mailbox mbox) {
/*  102 */     if (ZimbraLog.mbxmgr.isInfoEnabled())
/*  103 */       ZimbraLog.mbxmgr.info("Mailbox " + mbox.getId() + " account " + mbox.getAccountId() + " LOADED");
/*  104 */     for (Listener listener : this.mListeners)
/*  105 */       listener.mailboxLoaded(mbox);
/*      */   }
/*      */   
/*      */   private void notifyMailboxCreated(Mailbox mbox) {
/*  109 */     if (ZimbraLog.mbxmgr.isInfoEnabled())
/*  110 */       ZimbraLog.mbxmgr.info("Mailbox " + mbox.getId() + " account " + mbox.getAccountId() + " CREATED");
/*  111 */     for (Listener listener : this.mListeners)
/*  112 */       listener.mailboxCreated(mbox);
/*      */   }
/*      */   
/*      */   private void notifyMailboxDeleted(String accountId) {
/*  116 */     if (ZimbraLog.mbxmgr.isInfoEnabled())
/*  117 */       ZimbraLog.mbxmgr.info("Mailbox for account " + accountId + " DELETED");
/*  118 */     for (Listener listener : this.mListeners) {
/*  119 */       listener.mailboxDeleted(accountId);
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public MailboxManager()
/*      */     throws ServiceException
/*      */   {
/*  143 */     DbPool.DbConnection conn = null;
/*  144 */     synchronized (this) {
/*      */       try {
/*  146 */         conn = DbPool.getConnection();
/*  147 */         this.mailboxIds = DbMailbox.listMailboxes(conn, this);
/*  148 */         this.cache = createCache();
/*      */       } finally {
/*  150 */         DbPool.quietClose(conn);
/*      */       }
/*      */     }
/*      */   }
/*      */   
/*      */   protected MailboxMap createCache() {
/*  156 */     return new MailboxMap(LC.zimbra_mailbox_manager_hardref_cache.intValue());
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */   protected MailboxManager(boolean extend) {}
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */   public static synchronized MailboxManager getInstance()
/*      */     throws ServiceException
/*      */   {
/*  170 */     if (sInstance == null) {
/*  171 */       String className = LC.zimbra_class_mboxmanager.value();
/*  172 */       if ((className != null) && (!className.equals(""))) {
/*      */         try {
/*      */           try {
/*  175 */             sInstance = (MailboxManager)Class.forName(className).newInstance();
/*      */           }
/*      */           catch (ClassNotFoundException cnfe) {
/*  178 */             sInstance = (MailboxManager)ExtensionUtil.findClass(className).newInstance();
/*      */           }
/*      */         } catch (Exception e) {
/*  181 */           ZimbraLog.account.error("could not instantiate MailboxManager interface of class '" + className + "'; defaulting to MailboxManager", e);
/*      */         }
/*      */       }
/*  184 */       if (sInstance == null)
/*  185 */         sInstance = new MailboxManager();
/*      */     }
/*  187 */     return sInstance;
/*      */   }
/*      */   
/*      */   @VisibleForTesting
/*      */   public static void setInstance(MailboxManager mmgr) {
/*  192 */     sInstance = mmgr;
/*      */   }
/*      */   
/*      */   @VisibleForTesting
/*      */   public void clearCache() {
/*  197 */     this.cache.clear();
/*  198 */     this.mailboxIds.clear();
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public void startup() {}
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public void shutdown() {}
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */   public Mailbox getMailboxByAccount(Account account)
/*      */     throws ServiceException
/*      */   {
/*  219 */     return getMailboxByAccount(account, FetchMode.AUTOCREATE);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public Mailbox getMailboxByAccount(Account account, boolean autocreate)
/*      */     throws ServiceException
/*      */   {
/*  237 */     return getMailboxByAccount(account, autocreate ? FetchMode.AUTOCREATE : FetchMode.DO_NOT_AUTOCREATE);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public Mailbox getMailboxByAccount(Account account, FetchMode fetchMode)
/*      */     throws ServiceException
/*      */   {
/*  258 */     if (account == null)
/*  259 */       throw new IllegalArgumentException();
/*  260 */     return getMailboxByAccountId(account.getId(), fetchMode);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public Mailbox getMailboxByAccountId(String accountId)
/*      */     throws ServiceException
/*      */   {
/*  275 */     return getMailboxByAccountId(accountId, FetchMode.AUTOCREATE);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public Mailbox getMailboxByAccountId(String accountId, boolean autocreate)
/*      */     throws ServiceException
/*      */   {
/*  293 */     return getMailboxByAccountId(accountId, autocreate ? FetchMode.AUTOCREATE : FetchMode.DO_NOT_AUTOCREATE);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public Mailbox getMailboxByAccountId(String accountId, FetchMode fetchMode)
/*      */     throws ServiceException
/*      */   {
/*  314 */     return getMailboxByAccountId(accountId, fetchMode, false);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public Mailbox getMailboxByAccountId(String accountId, FetchMode fetchMode, boolean skipMailHostCheck)
/*      */     throws ServiceException
/*      */   {
/*  336 */     if (accountId == null) {
/*  337 */       throw new IllegalArgumentException();
/*      */     }
/*      */     Integer mailboxKey;
/*  340 */     synchronized (this) {
/*  341 */       mailboxKey = (Integer)this.mailboxIds.get(accountId.toLowerCase());
/*      */     }
/*  343 */     if (mailboxKey != null) {
/*  344 */       if (DebugConfig.mockMultiserverInstall)
/*  345 */         lookupAccountWithHostCheck(accountId, skipMailHostCheck);
/*  346 */       return getMailboxById(mailboxKey.intValue(), fetchMode, skipMailHostCheck); }
/*  347 */     if (fetchMode != FetchMode.AUTOCREATE) {
/*  348 */       return null;
/*      */     }
/*      */     
/*      */ 
/*  352 */     Account account = lookupAccountWithHostCheck(accountId, skipMailHostCheck);
/*  353 */     synchronized (this) {
/*  354 */       mailboxKey = (Integer)this.mailboxIds.get(accountId.toLowerCase());
/*      */     }
/*  356 */     if (mailboxKey != null) {
/*  357 */       return getMailboxById(mailboxKey.intValue(), fetchMode, skipMailHostCheck);
/*      */     }
/*  359 */     return createMailbox(null, account, skipMailHostCheck);
/*      */   }
/*      */   
/*      */   private Account lookupAccountWithHostCheck(String accountId, boolean skipMailHostCheck) throws ServiceException {
/*  363 */     Account account = Provisioning.getInstance().get(Key.AccountBy.id, accountId);
/*  364 */     if (account == null)
/*  365 */       throw AccountServiceException.NO_SUCH_ACCOUNT(accountId);
/*  366 */     if ((!skipMailHostCheck) && (!Provisioning.onLocalServer(account)))
/*  367 */       throw ServiceException.WRONG_HOST(account.getMailHost(), null);
/*  368 */     return account;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public Mailbox getMailboxById(int mailboxId)
/*      */     throws ServiceException
/*      */   {
/*  390 */     return getMailboxById(mailboxId, FetchMode.DO_NOT_AUTOCREATE, false);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   private Map<String, Integer> mailboxIds;
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   private MailboxMap cache;
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public Mailbox getMailboxById(int mailboxId, boolean skipMailHostCheck)
/*      */     throws ServiceException
/*      */   {
/*  419 */     return getMailboxById(mailboxId, FetchMode.DO_NOT_AUTOCREATE, skipMailHostCheck);
/*      */   }
/*      */   
/*      */ 
/*      */   protected Mailbox getMailboxById(int mailboxId, FetchMode fetchMode, boolean skipMailHostCheck)
/*      */     throws ServiceException
/*      */   {
/*  426 */     assert ((fetchMode == FetchMode.ONLY_IF_CACHED) || (!Thread.holdsLock(this)));
/*      */     
/*  428 */     if (mailboxId <= 0) {
/*  429 */       throw MailServiceException.NO_SUCH_MBOX(mailboxId);
/*      */     }
/*  431 */     long startTime = ZimbraPerf.STOPWATCH_MBOX_GET.start();
/*      */     
/*  433 */     Mailbox mbox = null;
/*  434 */     synchronized (this)
/*      */     {
/*  436 */       Object cached = retrieveFromCache(mailboxId, true);
/*  437 */       if ((cached instanceof Mailbox)) {
/*  438 */         ZimbraPerf.COUNTER_MBOX_CACHE.increment(100L);
/*  439 */         mbox = (Mailbox)cached;
/*      */       }
/*      */     }
/*      */     
/*  443 */     if ((fetchMode == FetchMode.ONLY_IF_CACHED) && ((mbox == null) || (!mbox.isOpen())))
/*      */     {
/*  445 */       return null;
/*      */     }
/*      */     
/*  448 */     if (mbox == null) {
/*  449 */       ZimbraPerf.COUNTER_MBOX_CACHE.increment(0L);
/*      */       
/*  451 */       DbPool.DbConnection conn = DbPool.getConnection();
/*      */       Mailbox.MailboxData data;
/*      */       try {
/*  454 */         data = DbMailbox.getMailboxStats(conn, mailboxId);
/*  455 */         if (data == null) {
/*  456 */           throw MailServiceException.NO_SUCH_MBOX(mailboxId);
/*      */         }
/*      */       } finally {
/*  459 */         conn.closeQuietly();
/*      */       }
/*      */       
/*  462 */       mbox = instantiateMailbox(data);
/*  463 */       Account account = mbox.getAccount();
/*  464 */       boolean isGalSyncAccount = AccountUtil.isGalSyncAccount(account);
/*  465 */       mbox.setGalSyncMailbox(isGalSyncAccount);
/*      */       
/*  467 */       if (!skipMailHostCheck)
/*      */       {
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*  474 */         if (!Provisioning.onLocalServer(account)) {
/*  475 */           throw ServiceException.WRONG_HOST(account.getMailHost(), null);
/*      */         }
/*      */       }
/*  478 */       synchronized (this)
/*      */       {
/*  480 */         Object cached = retrieveFromCache(mailboxId, false);
/*  481 */         if ((cached instanceof Mailbox)) {
/*  482 */           mbox = (Mailbox)cached;
/*      */ 
/*      */         }
/*  485 */         else if ((cached instanceof MailboxMaintenance)) {
/*  486 */           ((MailboxMaintenance)cached).setMailbox(mbox);
/*      */         } else {
/*  488 */           cacheMailbox(mbox);
/*      */         }
/*      */       }
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*      */ 
/*  496 */     if (mbox.open())
/*      */     {
/*  498 */       notifyMailboxLoaded(mbox);
/*      */     }
/*      */     
/*  501 */     ZimbraPerf.STOPWATCH_MBOX_GET.stop(startTime);
/*      */     
/*  503 */     if ((this.maintenanceLocks.containsKey(mbox.getAccountId())) && (mbox.getMaintenance() == null))
/*      */     {
/*      */ 
/*  506 */       MailboxMaintenance oldMaint = (MailboxMaintenance)this.maintenanceLocks.get(mbox.getAccountId());
/*  507 */       MailboxMaintenance maint = null;
/*  508 */       synchronized (mbox) {
/*  509 */         maint = mbox.beginMaintenance();
/*  510 */         synchronized (this) {
/*  511 */           this.cache.put(Integer.valueOf(mailboxId), maint);
/*      */         }
/*      */       }
/*  514 */       if (oldMaint.isNestedAllowed()) {
/*  515 */         maint.setNestedAllowed(true);
/*      */       }
/*  517 */       maint.removeAllowedThread(Thread.currentThread());
/*  518 */       this.maintenanceLocks.put(mbox.getAccountId(), maint);
/*      */     }
/*  520 */     return mbox;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */   public synchronized List<Mailbox> getAllLoadedMailboxes()
/*      */   {
/*  527 */     List<Mailbox> mboxes = new ArrayList(this.cache.size());
/*  528 */     for (Object o : this.cache.values()) {
/*  529 */       if ((o instanceof Mailbox)) {
/*  530 */         mboxes.add((Mailbox)o);
/*  531 */       } else if ((o instanceof MailboxMaintenance)) {
/*  532 */         MailboxMaintenance maintenance = (MailboxMaintenance)o;
/*  533 */         if (maintenance.canAccess()) {
/*  534 */           mboxes.add(maintenance.getMailbox());
/*      */         }
/*      */       }
/*      */     }
/*  538 */     return mboxes;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */   public synchronized int getCacheSize()
/*      */   {
/*  545 */     int count = 0;
/*  546 */     for (Object o : this.cache.values()) {
/*  547 */       if (((o instanceof Mailbox)) || ((o instanceof MailboxMaintenance))) {
/*  548 */         count++;
/*      */       }
/*      */     }
/*  551 */     return count;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public synchronized boolean isMailboxLoadedAndAvailable(int mailboxId)
/*      */   {
/*  560 */     Object cached = this.cache.get(Integer.valueOf(mailboxId));
/*  561 */     if (cached == null) {
/*  562 */       return false;
/*      */     }
/*  564 */     if ((cached instanceof MailboxMaintenance)) {
/*  565 */       return ((MailboxMaintenance)cached).canAccess();
/*      */     }
/*  567 */     return true;
/*      */   }
/*      */   
/*      */   private Object retrieveFromCache(int mailboxId, boolean trackGC) throws MailServiceException
/*      */   {
/*  572 */     synchronized (this) {
/*  573 */       Object cached = this.cache.get(Integer.valueOf(mailboxId), trackGC);
/*  574 */       if ((cached instanceof MailboxMaintenance)) {
/*  575 */         MailboxMaintenance maintenance = (MailboxMaintenance)cached;
/*  576 */         if (!maintenance.canAccess()) {
/*  577 */           if (isMailboxLockedOut(maintenance.getAccountId())) {
/*  578 */             throw MailServiceException.MAINTENANCE(mailboxId, "mailbox locked out for maintenance");
/*      */           }
/*  580 */           throw MailServiceException.MAINTENANCE(mailboxId);
/*      */         }
/*      */         
/*  583 */         if (maintenance.getMailbox() != null) {
/*  584 */           return maintenance.getMailbox();
/*      */         }
/*      */       }
/*      */       
/*  588 */       return cached;
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */   protected Mailbox instantiateMailbox(Mailbox.MailboxData data)
/*      */     throws ServiceException
/*      */   {
/*  596 */     return new Mailbox(data);
/*      */   }
/*      */   
/*      */   protected Mailbox instantiateExternalVirtualMailbox(Mailbox.MailboxData data) throws ServiceException {
/*  600 */     return new ExternalVirtualMailbox(data);
/*      */   }
/*      */   
/*      */   protected synchronized void cacheAccount(String accountId, int mailboxId) {
/*  604 */     this.mailboxIds.put(accountId.toLowerCase(), Integer.valueOf(mailboxId));
/*      */   }
/*      */   
/*      */   private Mailbox cacheMailbox(Mailbox mailbox) {
/*  608 */     this.cache.put(Integer.valueOf(mailbox.getId()), mailbox);
/*  609 */     return mailbox;
/*      */   }
/*      */   
/*      */   public MailboxMaintenance beginMaintenance(String accountId, int mailboxId) throws ServiceException {
/*  613 */     Mailbox mbox = getMailboxByAccountId(accountId, false);
/*  614 */     if (mbox == null) {
/*  615 */       synchronized (this) {
/*  616 */         if (this.mailboxIds.get(accountId.toLowerCase()) == null) {
/*  617 */           MailboxMaintenance maintenance = new MailboxMaintenance(accountId, mailboxId);
/*  618 */           this.cache.put(Integer.valueOf(mailboxId), maintenance);
/*  619 */           return maintenance;
/*      */         }
/*      */       }
/*  622 */       mbox = getMailboxByAccountId(accountId);
/*      */     }
/*      */     
/*      */ 
/*  626 */     mbox.lock.lock();
/*      */     try {
/*  628 */       MailboxMaintenance maintenance = mbox.beginMaintenance();
/*  629 */       synchronized (this) {
/*  630 */         this.cache.put(Integer.valueOf(mailboxId), maintenance);
/*      */       }
/*  632 */       return maintenance;
/*      */     } finally {
/*  634 */       mbox.lock.release();
/*      */     }
/*      */   }
/*      */   
/*      */   public void endMaintenance(MailboxMaintenance maintenance, boolean success, boolean removeFromCache) throws ServiceException
/*      */   {
/*  640 */     Preconditions.checkNotNull(maintenance);
/*      */     
/*  642 */     Mailbox availableMailbox = null;
/*      */     
/*  644 */     synchronized (this) {
/*  645 */       Object obj = this.cache.get(Integer.valueOf(maintenance.getMailboxId()));
/*  646 */       if (obj != maintenance) {
/*  647 */         ZimbraLog.mailbox.debug("maintenance ended with wrong object. passed %s; expected %s", new Object[] { maintenance, obj });
/*  648 */         throw MailServiceException.MAINTENANCE(maintenance.getMailboxId(), "attempting to end maintenance with wrong object");
/*      */       }
/*      */       
/*  651 */       this.cache.remove(Integer.valueOf(maintenance.getMailboxId()));
/*      */       
/*  653 */       Mailbox mbox = maintenance.getMailbox();
/*  654 */       if (success)
/*      */       {
/*  656 */         cacheAccount(maintenance.getAccountId(), maintenance.getMailboxId());
/*      */         
/*  658 */         if (mbox != null) {
/*  659 */           assert ((maintenance == mbox.getMaintenance()) || (mbox.getMaintenance() == null));
/*      */           
/*  661 */           if (removeFromCache) {
/*  662 */             mbox.purge(MailItem.Type.UNKNOWN);
/*      */             
/*      */ 
/*  665 */             IndexStore index = mbox.index.getIndexStore();
/*  666 */             if (index != null) {
/*  667 */               index.evict();
/*      */             }
/*      */             
/*      */           }
/*  671 */           else if (mbox.endMaintenance(success)) {
/*  672 */             ZimbraLog.mailbox.debug("no longer in maintenace; caching mailbox");
/*  673 */             cacheMailbox(maintenance.getMailbox());
/*      */           } else {
/*  675 */             ZimbraLog.mailbox.debug("still in maintenance; caching lock");
/*  676 */             this.cache.put(Integer.valueOf(mbox.getId()), mbox.getMaintenance());
/*      */           }
/*      */           
/*  679 */           availableMailbox = mbox;
/*      */         }
/*      */       }
/*      */       else {
/*  683 */         if (mbox != null) {
/*  684 */           mbox.endMaintenance(success);
/*      */         }
/*  686 */         maintenance.markUnavailable();
/*      */       }
/*      */     }
/*      */     
/*  690 */     if (availableMailbox != null) {
/*  691 */       notifyMailboxAvailable(availableMailbox);
/*      */     }
/*      */   }
/*      */   
/*      */   /* Error */
/*      */   public int getMailboxCount()
/*      */   {
/*      */     // Byte code:
/*      */     //   0: aload_0
/*      */     //   1: dup
/*      */     //   2: astore_1
/*      */     //   3: monitorenter
/*      */     //   4: aload_0
/*      */     //   5: getfield 41	com/zimbra/cs/mailbox/MailboxManager:mailboxIds	Ljava/util/Map;
/*      */     //   8: invokeinterface 171 1 0
/*      */     //   13: aload_1
/*      */     //   14: monitorexit
/*      */     //   15: ireturn
/*      */     //   16: astore_2
/*      */     //   17: aload_1
/*      */     //   18: monitorexit
/*      */     //   19: aload_2
/*      */     //   20: athrow
/*      */     // Line number table:
/*      */     //   Java source line #700	-> byte code offset #0
/*      */     //   Java source line #701	-> byte code offset #4
/*      */     //   Java source line #702	-> byte code offset #16
/*      */     // Local variable table:
/*      */     //   start	length	slot	name	signature
/*      */     //   0	21	0	this	MailboxManager
/*      */     //   2	16	1	Ljava/lang/Object;	Object
/*      */     //   16	4	2	localObject1	Object
/*      */     // Exception table:
/*      */     //   from	to	target	type
/*      */     //   4	15	16	finally
/*      */     //   16	19	16	finally
/*      */   }
/*      */   
/*      */   public int[] getMailboxIds()
/*      */   {
/*  711 */     int i = 0;
/*  712 */     synchronized (this) {
/*  713 */       Collection<Integer> col = this.mailboxIds.values();
/*  714 */       int[] mailboxIds = new int[col.size()];
/*  715 */       for (Iterator i$ = col.iterator(); i$.hasNext();) { int id = ((Integer)i$.next()).intValue();
/*  716 */         mailboxIds[(i++)] = id; }
/*  717 */       return mailboxIds;
/*      */     }
/*      */   }
/*      */   
/*      */   public Set<Integer> getPurgePendingMailboxes(long time) throws ServiceException {
/*  722 */     DbPool.DbConnection conn = null;
/*      */     try {
/*  724 */       conn = DbPool.getConnection();
/*  725 */       return DbMailbox.listPurgePendingMailboxes(conn, time);
/*      */     } finally {
/*  727 */       DbPool.quietClose(conn);
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public String[] getAccountIds()
/*      */   {
/*  737 */     int i = 0;
/*  738 */     synchronized (this) {
/*  739 */       Set<String> set = this.mailboxIds.keySet();
/*  740 */       String[] accountIds = new String[set.size()];
/*  741 */       for (String o : set)
/*  742 */         accountIds[(i++)] = o;
/*  743 */       return accountIds;
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */   public int lookupMailboxId(String accountId)
/*      */   {
/*      */     Integer v;
/*      */     
/*      */ 
/*  755 */     synchronized (this) {
/*  756 */       v = (Integer)this.mailboxIds.get(accountId);
/*      */     }
/*  758 */     return v != null ? v.intValue() : -1;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public Map<String, Long> getMailboxSizes(List<NamedEntry> accounts)
/*      */     throws ServiceException
/*      */   {
/*      */     List<Integer> requested;
/*      */     
/*      */ 
/*      */ 
/*  772 */     synchronized (this) { List<Integer> requested;
/*  773 */       if (accounts == null) {
/*  774 */         requested = new ArrayList(this.mailboxIds.values());
/*      */       } else {
/*  776 */         requested = new ArrayList(accounts.size());
/*  777 */         for (NamedEntry account : accounts) {
/*  778 */           Integer mailboxId = (Integer)this.mailboxIds.get(account.getId());
/*  779 */           if (mailboxId != null) {
/*  780 */             requested.add(mailboxId);
/*      */           }
/*      */         }
/*      */       }
/*      */     }
/*  785 */     DbPool.DbConnection conn = null;
/*      */     try {
/*  787 */       conn = DbPool.getConnection();
/*  788 */       return DbMailbox.getMailboxSizes(conn, requested);
/*      */     } finally {
/*  790 */       if (conn != null) {
/*  791 */         DbPool.quietClose(conn);
/*      */       }
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public Mailbox createMailbox(OperationContext octxt, Account account)
/*      */     throws ServiceException
/*      */   {
/*  812 */     return createMailbox(octxt, account, false);
/*      */   }
/*      */   
/*      */   private Mailbox createMailbox(OperationContext octxt, Account account, boolean skipMailHostCheck) throws ServiceException {
/*  816 */     if (account == null)
/*  817 */       throw ServiceException.FAILURE("createMailbox: must specify an account", null);
/*  818 */     if ((!skipMailHostCheck) && (!Provisioning.onLocalServer(account))) {
/*  819 */       throw ServiceException.WRONG_HOST(account.getMailHost(), null);
/*      */     }
/*      */     
/*  822 */     Mailbox mbox = null;
/*  823 */     Integer mailboxKey = null;
/*      */     do {
/*  825 */       if (mailboxKey != null) {
/*  826 */         return getMailboxById(mailboxKey.intValue());
/*      */       }
/*  828 */       boolean isGalSyncAccount = AccountUtil.isGalSyncAccount(account);
/*  829 */       synchronized (this)
/*      */       {
/*  831 */         mailboxKey = (Integer)this.mailboxIds.get(account.getId().toLowerCase());
/*  832 */         if (mailboxKey == null)
/*      */         {
/*      */ 
/*  835 */           DbPool.DbConnection conn = null;
/*      */           try {
/*  837 */             conn = DbPool.getConnection();
/*  838 */             mailboxKey = Integer.valueOf(DbMailbox.getMailboxId(conn, account.getId()));
/*  839 */             if ((mailboxKey != null) && (mailboxKey.intValue() > 0)) {
/*  840 */               cacheAccount(account.getId(), mailboxKey.intValue());
/*      */               
/*      */ 
/*      */ 
/*  844 */               DbPool.quietClose(conn); continue; } } finally { DbPool.quietClose(conn);
/*      */           }
/*      */           
/*  847 */           mbox = createMailboxInternal(octxt, account, isGalSyncAccount);
/*      */         }
/*  849 */       } } while (mbox == null);
/*      */     
/*      */ 
/*      */ 
/*  853 */     if (mbox.open()) {
/*  854 */       notifyMailboxCreated(mbox);
/*      */     }
/*  856 */     return mbox;
/*      */   }
/*      */   
/*      */   private synchronized Mailbox createMailboxInternal(OperationContext octxt, Account account, boolean isGalSyncAccount) throws ServiceException {
/*  860 */     CreateMailbox redoRecorder = new CreateMailbox(account.getId());
/*      */     
/*  862 */     Mailbox mbox = null;
/*  863 */     boolean success = false;
/*  864 */     DbPool.DbConnection conn = DbPool.getConnection();
/*      */     try {
/*  866 */       CreateMailbox redoPlayer = octxt == null ? null : (CreateMailbox)octxt.getPlayer();
/*  867 */       int id = redoPlayer == null ? -1 : redoPlayer.getMailboxId();
/*      */       
/*      */ 
/*      */ 
/*  871 */       boolean created = false;
/*      */       try {
/*  873 */         data = DbMailbox.createMailbox(conn, id, account.getId(), account.getName(), -1);
/*  874 */         ZimbraLog.mailbox.info("Creating mailbox with id %d and group id %d for %s.", new Object[] { Integer.valueOf(data.id), Integer.valueOf(data.schemaGroupId), account.getName() });
/*  875 */         created = true;
/*      */       } catch (ServiceException se) { Mailbox.MailboxData data;
/*  877 */         if ("mail.ALREADY_EXISTS".equals(se.getCode()))
/*      */         {
/*  879 */           id = DbMailbox.getMailboxId(conn, account.getId());
/*  880 */           if (id > 0) {
/*  881 */             data = DbMailbox.getMailboxStats(conn, id);
/*      */           } else {
/*  883 */             throw ServiceException.FAILURE("could not create mailbox", se);
/*      */           }
/*      */         } else {
/*  886 */           throw se;
/*      */         } }
/*      */       Mailbox.MailboxData data;
/*  889 */       mbox = account.isIsExternalVirtualAccount() ? instantiateExternalVirtualMailbox(data) : instantiateMailbox(data);
/*      */       
/*  891 */       mbox.setGalSyncMailbox(isGalSyncAccount);
/*      */       
/*  893 */       mbox.beginTransaction("createMailbox", octxt, redoRecorder, conn);
/*      */       
/*  895 */       if (created)
/*      */       {
/*  897 */         mbox.initialize();
/*      */       }
/*      */       
/*      */ 
/*  901 */       cacheAccount(data.accountId, data.id);
/*  902 */       cacheMailbox(mbox);
/*  903 */       redoRecorder.setMailboxId(mbox.getId());
/*      */       
/*  905 */       success = true;
/*      */     }
/*      */     catch (ServiceException e)
/*      */     {
/*  909 */       ZimbraLog.mailbox.error("Error during mailbox creation", e);
/*  910 */       throw e;
/*      */     } catch (OutOfMemoryError e) {
/*  912 */       Zimbra.halt("out of memory", e);
/*      */     } catch (Throwable t) {
/*  914 */       ZimbraLog.mailbox.error("Error during mailbox creation", t);
/*  915 */       throw ServiceException.FAILURE("createMailbox", t);
/*      */     } finally {
/*      */       try {
/*  918 */         if (mbox != null) {
/*  919 */           mbox.endTransaction(success);
/*      */         } else {
/*  921 */           conn.rollback();
/*      */         }
/*      */       } finally {
/*  924 */         conn.closeQuietly();
/*      */       }
/*      */     }
/*      */     
/*  928 */     return mbox;
/*      */   }
/*      */   
/*      */   protected void markMailboxDeleted(Mailbox mailbox) {
/*  932 */     String accountId = mailbox.getAccountId().toLowerCase();
/*  933 */     synchronized (this) {
/*  934 */       this.maintenanceLocks.remove(accountId);
/*  935 */       this.mailboxIds.remove(accountId);
/*  936 */       this.cache.remove(Integer.valueOf(mailbox.getId()));
/*      */     }
/*  938 */     notifyMailboxDeleted(accountId);
/*      */   }
/*      */   
/*      */   public void dumpMailboxCache() {
/*  942 */     StringBuilder sb = new StringBuilder();
/*  943 */     sb.append("MAILBOX CACHE DUMPS\n");
/*  944 */     sb.append("----------------------------------------------------------------------\n");
/*  945 */     synchronized (this) {
/*  946 */       for (Map.Entry<String, Integer> entry : this.mailboxIds.entrySet())
/*  947 */         sb.append("1) key=" + (String)entry.getKey() + " (hash=" + ((String)entry.getKey()).hashCode() + "); val=" + entry.getValue() + "\n");
/*  948 */       for (Map.Entry<Integer, Object> entry : this.cache.entrySet())
/*  949 */         sb.append("2) key=" + entry.getKey() + "; val=" + entry.getValue() + "(class= " + entry.getValue().getClass().getName() + ",hash=" + entry.getValue().hashCode() + ")");
/*      */     }
/*  951 */     sb.append("----------------------------------------------------------------------\n");
/*  952 */     ZimbraLog.mailbox.debug(sb.toString());
/*      */   }
/*      */   
/*      */   public void lockoutMailbox(String accountId) throws ServiceException {
/*  956 */     ZimbraLog.mailbox.debug("locking out mailbox for account %s", new Object[] { accountId });
/*  957 */     Mailbox mbox = getMailboxByAccountId(accountId);
/*  958 */     MailboxMaintenance maintenance = beginMaintenance(accountId, mbox.getId());
/*  959 */     maintenance.setNestedAllowed(true);
/*  960 */     maintenance.removeAllowedThread(Thread.currentThread());
/*  961 */     this.maintenanceLocks.put(mbox.getAccountId(), maintenance);
/*      */   }
/*      */   
/*      */   public void undoLockout(String accountId, boolean endMaintenance) throws ServiceException {
/*  965 */     ZimbraLog.mailbox.debug("undoing lockout for account %s", new Object[] { accountId });
/*  966 */     MailboxMaintenance maintenance = (MailboxMaintenance)this.maintenanceLocks.remove(accountId);
/*  967 */     if (maintenance == null) {
/*  968 */       throw ServiceException.NOT_FOUND("No lock known for account " + accountId, null);
/*      */     }
/*  970 */     if (endMaintenance) {
/*  971 */       maintenance.registerAllowedThread(Thread.currentThread());
/*  972 */       endMaintenance(maintenance, true, false);
/*      */     }
/*      */   }
/*      */   
/*      */   public void undoLockout(String accountId) throws ServiceException {
/*  977 */     undoLockout(accountId, true);
/*      */   }
/*      */   
/*      */   public boolean isMailboxLockedOut(String accountId) {
/*  981 */     if (ZimbraLog.mailbox.isDebugEnabled()) {
/*  982 */       ZimbraLog.mailbox.debug("Checking is locked for account %s? %s", new Object[] { accountId, Boolean.valueOf(this.maintenanceLocks.containsKey(accountId)) });
/*      */     }
/*  984 */     return this.maintenanceLocks.containsKey(accountId);
/*      */   }
/*      */   
/*      */   public void registerOuterMaintenanceThread(String accountId) throws MailServiceException {
/*  988 */     ZimbraLog.mailbox.debug("registering maintenance thread for account %s", new Object[] { accountId });
/*  989 */     MailboxMaintenance maintenance = (MailboxMaintenance)this.maintenanceLocks.get(accountId);
/*  990 */     if (maintenance != null) {
/*  991 */       maintenance.registerOuterAllowedThread(Thread.currentThread());
/*      */     }
/*      */   }
/*      */   
/*      */   public void unregisterMaintenanceThread(String accountId) {
/*  996 */     ZimbraLog.mailbox.debug("unregistering maintenance thread for account %s", new Object[] { accountId });
/*  997 */     MailboxMaintenance maintenance = (MailboxMaintenance)this.maintenanceLocks.get(accountId);
/*  998 */     if (maintenance != null)
/*  999 */       maintenance.removeAllowedThread(Thread.currentThread()); }
/*      */   
/*      */   public static abstract interface Listener { public abstract void mailboxAvailable(Mailbox paramMailbox);
/*      */     
/*      */     public abstract void mailboxLoaded(Mailbox paramMailbox);
/*      */     
/*      */     public abstract void mailboxCreated(Mailbox paramMailbox);
/*      */     
/*      */     public abstract void mailboxDeleted(String paramString); }
/*      */   
/* 1009 */   protected static class MailboxMap implements Map<Integer, Object> { MailboxMap(int hardSize) { hardSize = Math.max(hardSize, 0);
/* 1010 */       this.mHardSize = hardSize;
/* 1011 */       this.mSoftMap = new HashMap();
/* 1012 */       this.mHardMap = new LinkedHashMap(this.mHardSize / 4, 0.75F, true) {
/*      */         protected boolean removeEldestEntry(Map.Entry<Integer, Object> eldest) {
/* 1014 */           if (size() <= MailboxManager.MailboxMap.this.mHardSize) {
/* 1015 */             return false;
/*      */           }
/* 1017 */           Object obj = eldest.getValue();
/* 1018 */           if ((obj instanceof Mailbox))
/* 1019 */             obj = new SoftReference((Mailbox)obj);
/* 1020 */           MailboxManager.MailboxMap.this.mSoftMap.put(eldest.getKey(), obj);
/* 1021 */           return true;
/*      */         } }; }
/*      */     
/*      */     final int mHardSize;
/*      */     final LinkedHashMap<Integer, Object> mHardMap;
/*      */     final HashMap<Integer, Object> mSoftMap;
/* 1027 */     protected MailboxMap() { this.mHardSize = 0;
/* 1028 */       this.mHardMap = null;
/* 1029 */       this.mSoftMap = null;
/*      */     }
/*      */     
/*      */     public void clear() {
/* 1033 */       this.mHardMap.clear();
/* 1034 */       this.mSoftMap.clear();
/*      */     }
/*      */     
/*      */     public boolean containsKey(Object key) {
/* 1038 */       return (this.mHardMap.containsKey(key)) || (this.mSoftMap.containsKey(key));
/*      */     }
/*      */     
/*      */     public boolean containsValue(Object value) {
/* 1042 */       return (this.mHardMap.containsValue(value)) || (this.mSoftMap.containsValue(value));
/*      */     }
/*      */     
/*      */     public Set<Map.Entry<Integer, Object>> entrySet() {
/* 1046 */       Set<Map.Entry<Integer, Object>> entries = new HashSet(size());
/* 1047 */       if (this.mHardSize > 0)
/* 1048 */         entries.addAll(this.mHardMap.entrySet());
/* 1049 */       entries.addAll(this.mSoftMap.entrySet());
/* 1050 */       return entries;
/*      */     }
/*      */     
/*      */     public Object get(Object key) {
/* 1054 */       return get(key, false);
/*      */     }
/*      */     
/*      */     public Object get(Object key, boolean trackGC) {
/* 1058 */       Object obj = this.mHardSize > 0 ? this.mHardMap.get(key) : null;
/* 1059 */       if (obj == null) {
/* 1060 */         obj = this.mSoftMap.get(key);
/* 1061 */         if ((obj instanceof SoftReference)) {
/* 1062 */           obj = ((SoftReference)obj).get();
/* 1063 */           if ((trackGC) && (obj == null))
/* 1064 */             ZimbraLog.mailbox.debug("mailbox " + key + " has been GCed; reloading");
/*      */         }
/*      */       }
/* 1067 */       return obj;
/*      */     }
/*      */     
/*      */     public boolean isEmpty() {
/* 1071 */       return (this.mHardMap.isEmpty()) && (this.mSoftMap.isEmpty());
/*      */     }
/*      */     
/*      */     public Set<Integer> keySet() {
/* 1075 */       Set<Integer> keys = new HashSet(size());
/* 1076 */       if (this.mHardSize > 0)
/* 1077 */         keys.addAll(this.mHardMap.keySet());
/* 1078 */       keys.addAll(this.mSoftMap.keySet());
/* 1079 */       return keys;
/*      */     }
/*      */     
/*      */     public Object put(Integer key, Object value) {
/*      */       Object removed;
/* 1084 */       if (this.mHardSize > 0) {
/* 1085 */         Object removed = this.mHardMap.put(key, value);
/* 1086 */         if (removed == null)
/* 1087 */           removed = this.mSoftMap.remove(key);
/*      */       } else {
/* 1089 */         if ((value instanceof Mailbox))
/* 1090 */           value = new SoftReference(value);
/* 1091 */         removed = this.mSoftMap.put(key, value);
/*      */       }
/* 1093 */       if ((removed instanceof SoftReference))
/* 1094 */         removed = ((SoftReference)removed).get();
/* 1095 */       return removed;
/*      */     }
/*      */     
/*      */     public void putAll(Map<? extends Integer, ? extends Object> t) {
/* 1099 */       for (Map.Entry<? extends Integer, ? extends Object> entry : t.entrySet())
/* 1100 */         put((Integer)entry.getKey(), entry.getValue());
/*      */     }
/*      */     
/*      */     public Object remove(Object key) {
/* 1104 */       Object removed = this.mHardSize > 0 ? this.mHardMap.remove(key) : null;
/* 1105 */       if (removed == null) {
/* 1106 */         removed = this.mSoftMap.remove(key);
/* 1107 */         if ((removed instanceof SoftReference))
/* 1108 */           removed = ((SoftReference)removed).get();
/*      */       }
/* 1110 */       return removed;
/*      */     }
/*      */     
/*      */     public int size() {
/* 1114 */       return this.mHardMap.size() + this.mSoftMap.size();
/*      */     }
/*      */     
/*      */     public Collection<Object> values() {
/* 1118 */       List<Object> values = new ArrayList(size());
/* 1119 */       if (this.mHardSize > 0)
/* 1120 */         values.addAll(this.mHardMap.values());
/* 1121 */       for (Object o : this.mSoftMap.values()) {
/* 1122 */         if ((o instanceof SoftReference))
/* 1123 */           o = ((SoftReference)o).get();
/* 1124 */         values.add(o);
/*      */       }
/* 1126 */       return values;
/*      */     }
/*      */     
/*      */     public String toString() {
/* 1130 */       return "<" + this.mHardMap.toString() + ", " + this.mSoftMap.toString() + ">";
/*      */     }
/*      */   }
/*      */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/mailbox/MailboxManager.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */